﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Media.Imaging;
using System.Runtime.InteropServices.WindowsRuntime;

using EquationRenderer.GraphEngine;
using FunctionGraph;
using Windows.UI;
using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;
using Windows.Graphics.Display;

namespace EquationRenderer
{
    public sealed partial class MainPage : SwapChainBackgroundPanel
    {
        Engine engine;
        double offsetX, offsetY, scale;
        int version;
        object pixelLock = new object();

        public MainPage()
        {
            this.InitializeComponent();
            version = 0;
            offsetX = 0;
            offsetY = 0;
            scale = 0.01;
        }

        private void InitializeRenderContext()
        {
            int width = (int)Window.Current.CoreWindow.Bounds.Width;
            int height = (int)Window.Current.CoreWindow.Bounds.Height;
            engine = new Engine(Window.Current.CoreWindow, this, DisplayProperties.LogicalDpi, 16);
        }

        private void TextBox_TextChanged_1(object sender, TextChangedEventArgs e)
        {
            RenderNewGraph();
        }

        private void RenderNewGraph()
        {
            var expr = Compiler.Compile(txtEquation.Text);
            if (expr != null)
            {
                int w = (int)Window.Current.CoreWindow.Bounds.Width;
                int h = (int)Window.Current.CoreWindow.Bounds.Height;
                version = engine.EnqueueRenderEquation(expr.Serialize(), -w * 0.5 + offsetX, offsetY - engine.Height + h * 0.5, scale);
            }
        }

        private void Page_Unloaded_1(object sender, RoutedEventArgs e)
        {
            engine.Dispose();
        }

        private void ContentControl_SizeChanged_1(object sender, SizeChangedEventArgs e)
        {
            if (ActualHeight > 0.0 && engine != null)
            {
                version = engine.EnqueueResize((int)ActualWidth, (int)ActualHeight);
            }
        }

        private void Grid_Loaded_1(object sender, RoutedEventArgs e)
        {
            InitializeRenderContext();
            txtEquation.Focus(Windows.UI.Xaml.FocusState.Keyboard);
            engine.Refresh();
        }

        CompositeTransform imageTransform = new CompositeTransform();
        private void imageControl_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
        {
            imageTransform.TranslateX = (int)e.Cumulative.Translation.X;
            imageTransform.TranslateY = (int)e.Cumulative.Translation.Y;

            imageTransform.CenterX = e.Position.X;
            imageTransform.CenterY = e.Position.Y;
            imageTransform.ScaleX = imageTransform.ScaleY = e.Cumulative.Scale;
            engine.SetRenderTransform(imageTransform.TranslateX, imageTransform.TranslateY, imageTransform.ScaleX, imageTransform.ScaleY);
            engine.Refresh();
        }

        private void imageControl_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
        {
        }

        private void imageControl_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
        {
            double oldOffsetX = offsetX;
            double oldOffsetY = offsetY;
            offsetX = (-ActualWidth * 0.5 + offsetX + e.Position.X) * imageTransform.ScaleX - e.Position.X + ActualWidth * 0.5;
            offsetY = (ActualHeight * 0.5 + offsetY - e.Position.Y) * imageTransform.ScaleX + e.Position.Y - ActualHeight * 0.5;
            offsetX -= e.Cumulative.Translation.X * imageTransform.ScaleX;
            offsetY += e.Cumulative.Translation.Y * imageTransform.ScaleX;
            offsetX = Math.Floor(offsetX);
            offsetY = Math.Floor(offsetY);
            if (e.Cumulative.Scale == 1.0f)
            {
                version = engine.EnqueueMoveView(offsetX - oldOffsetX, offsetY - oldOffsetY, -ActualWidth * 0.5 + offsetX, offsetY - engine.Height + ActualHeight * 0.5, scale);
            }
            else
            {
                scale /= imageTransform.ScaleX;
                RenderNewGraph();
            }
            engine.ResetRenderTransform();
        }

        private void imageControl_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
        {
            double dscale = 1.0;
            var p = e.GetCurrentPoint(this);
            if (p.Properties.MouseWheelDelta > 0)
                dscale = 1.0 / 0.8;
            else
                dscale = 0.8;
            offsetX = (-ActualWidth * 0.5 + offsetX + p.Position.X) * dscale - p.Position.X + ActualWidth * 0.5;
            offsetY = (ActualHeight * 0.5 + offsetY - p.Position.Y) * dscale + p.Position.Y - ActualHeight * 0.5;
            scale /= dscale;

            RenderNewGraph();
        }

        private void imageControl_ManipulationInertiaStarting(object sender, ManipulationInertiaStartingRoutedEventArgs e)
        {
            e.TranslationBehavior.DesiredDeceleration = 10.0f;
            e.TranslationBehavior.DesiredDisplacement = 0.5 * Math.Sqrt(e.Velocities.Linear.X * e.Velocities.Linear.X + e.Velocities.Linear.Y * e.Velocities.Linear.Y);
            e.Handled = true;
        }

        
    }
}
